package com.dg.dgrmi.net;

import java.io.IOException;
import java.net.ServerSocket;
import java.nio.BufferUnderflowException;
import java.nio.channels.ClosedChannelException;
import java.util.LinkedList;
import java.util.List;

import com.dg.dgrmi.handler.*;
import com.dg.dgrmi.handler.filter.DefaultFilter;
import com.dg.dgrmi.handler.filter.IProtocolFilter;
import org.xsocket.MaxReadSizeExceededException;
import org.xsocket.connection.*;
import org.xsocket.connection.IConnection.FlushMode;


/**
 * The LipeRMI server.
 * This object listen to a specific port and
 * when a client connects it delegates the connection
 * to a {@link net.sf.lipermi.handler.ConnectionHandler ConnectionHandler}.
 *
 * @author lipe
 * @date   05/10/2006
 *
 * @see    net.sf.lipermi.handler.CallHandler
 * @see    net.sf.lipermi.net.Client
 */
public class DGRMIServer implements IDataHandler,
        IDisconnectHandler, IConnectHandler {

    private ServerSocket serverSocket;

    private boolean enabled;

    private CallHandler callHandler;
    private IProtocolFilter filter;
    private List<IDGRMIServerListener> listeners = new LinkedList<
            IDGRMIServerListener>();

    public void addServerListener(IDGRMIServerListener listener) {
        listeners.add(listener);
    }

    public void removeServerListener(IDGRMIServerListener listener) {
        listeners.remove(listener);
    }

    public void close() {
        enabled = false;
    }

    public void bind(int port, CallHandler callHandler) throws Exception {
        bind(port, callHandler, new DefaultFilter());
    }

    public void bind(final int port, final CallHandler callHandler,
                     final IProtocolFilter filter) throws Exception {
        enabled = true;
        this.callHandler = callHandler;
        this.filter = filter;
        System.setProperty(
                "org.xsocket.connection.client.readbuffer.usedirect",
                "true");
        System.setProperty(
                "org.xsocket.connection.server.readbuffer.usedirect",
                "true");
        int threadqueuecapacity = Runtime.getRuntime().availableProcessors() *
                                  2 +
                                  1;
        System.setProperty(
                "org.xsocket.connection.server.workerpoolSize ",
                "" + threadqueuecapacity);
        final IServer srv = new Server(port, this);
        srv.setFlushMode(FlushMode.ASYNC);
        srv.setAutoflush(false);
        ConnectionUtils.registerMBean(srv);

        Thread bindThread = new Thread(new Runnable() {
            public void run() {

                srv.run(); // the call will not return

                try {
                    srv.start(); // returns after the server has been started
                } catch (IOException ex) {
                }
            }

        }, String.format("Bind (%d)", port)); //$NON-NLS-1$ //$NON-NLS-2$
        bindThread.start();

    }

    public boolean onDisconnect(final INonBlockingConnection connection) throws
            IOException {
//        ConnectionHandler hendler = (ConnectionHandler) connection.
//                                    getCustomSessionObject();
        for (IDGRMIServerListener listener : listeners) {
            listener.clientDisconnected(connection);
        }

        return false;
    }

    public boolean onData(final INonBlockingConnection connection) throws
            BufferUnderflowException, IOException, ClosedChannelException,
            MaxReadSizeExceededException {
        ConnectionHandler hendler = (ConnectionHandler) connection.
                                    getCustomSessionObject();
        hendler.messageResieved();
        return false;
    }

    public boolean onConnect(INonBlockingConnection connection) throws
            BufferUnderflowException, IOException, MaxReadSizeExceededException {
        final INonBlockingConnection _connection = connection;
        connection.setCustomSessionObject(ConnectionHandler.
                                          createConnectionHandler(_connection,
                callHandler,
                filter,
                new
                IConnectionHandlerListener() {

            public void connectionClosed() {
                for (IDGRMIServerListener listener : listeners) {
                    listener.clientDisconnected(_connection);
                }
            }

        }));

        for (IDGRMIServerListener listener : listeners) {
            listener.clientConnected(connection);
        }

        return false;
    }

}
